package edu.mse.camel.client.whiteboard.gef.ui.editpart;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FreeformLayer;
import org.eclipse.draw2d.FreeformLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.MarginBorder;
import org.eclipse.draw2d.Polyline;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.DirectEditPolicy;
import org.eclipse.gef.editpolicies.RootComponentEditPolicy;
import org.eclipse.gef.editpolicies.XYLayoutEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.requests.DirectEditRequest;

import edu.mse.camel.client.log.Debug;
import edu.mse.camel.client.whiteboard.gef.ui.WhiteboardPlugin;
import edu.mse.camel.client.whiteboard.gef.ui.commands.ChangeTextCommand;
import edu.mse.camel.client.whiteboard.gef.ui.commands.FreeWritingShapeCreateCommand;
import edu.mse.camel.client.whiteboard.gef.ui.commands.FreeWritingShapeSetConstraintCommand;
import edu.mse.camel.client.whiteboard.gef.ui.commands.ShapeCreateCommand;
import edu.mse.camel.client.whiteboard.gef.ui.commands.ShapeSetConstraintCommand;
import edu.mse.camel.client.whiteboard.gef.ui.figures.StickyNoteFigure;
import edu.mse.camel.client.whiteboard.gef.ui.freehandwriting.FreeHandwritingCreateRequest;
import edu.mse.camel.client.whiteboard.gef.whiteboard.EllipticalShape;
import edu.mse.camel.client.whiteboard.gef.whiteboard.StickyNote;
import edu.mse.camel.client.whiteboard.gef.whiteboard.FreeWritingShape;
import edu.mse.camel.client.whiteboard.gef.whiteboard.RectangularShape;
import edu.mse.camel.client.whiteboard.gef.whiteboard.Shape;
import edu.mse.camel.client.whiteboard.gef.whiteboard.StickyNote;
import edu.mse.camel.client.whiteboard.gef.whiteboard.WhiteboardDiagram;
import edu.mse.camel.client.whiteboard.gef.whiteboard.WhiteboardPackage;
import edu.mse.camel.client.whiteboard.gef.whiteboard.XY;
/**
 * EditPart for the a ShapesDiagram instance.
 * <p>This edit part server as the main diagram container, the white area where
 * everything else is in. Also responsible for the container's layout (the
 * way the container rearanges is contents) and the container's capabilities
 * (edit policies).
 * </p>
 * <p>This edit part must implement the PropertyChangeListener interface, 
 * so it can be notified of property changes in the corresponding model element.
 * </p>
 * 
 * @author Elias Volanakis
 */
public class WhiteboardDiagramEditPart extends AbstractGraphicalEditPart 
implements Adapter, IWhiteboardEditPart {

	public AbstractGraphicalEditPart getEditPart() {
		return this;
	}
	
	public WhiteboardDiagramEditPart(Object model) {
	//	super(model);
		this.setModel(model);
	}

	private Notifier target;
	/**
	 * Upon activation, attach to the model element as a property change listener.
	 */
	public void activate() {
		if (!isActive()) {
			hookIntoModel(getCastedModel());
			super.activate();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
	 */
	protected void createEditPolicies() {
		// disallows the removal of this edit part from its parent
		installEditPolicy(EditPolicy.COMPONENT_ROLE, new RootComponentEditPolicy());
		// handles constraint changes (e.g. moving and/or resizing) of model elements
		// and creation of new model elements
		installEditPolicy(EditPolicy.LAYOUT_ROLE,  new ShapesXYLayoutEditPolicy());
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
	 */
	protected IFigure createFigure() {
		Figure f = new FreeformLayer();
		f.setBorder(new MarginBorder(3));
		f.setLayoutManager(new FreeformLayout());
		return f;
	}

	/**
	 * Upon deactivation, detach from the model element as a property change listener.
	 */
	public void deactivate() {
		if (isActive()) {
			unhookFromModel(getCastedModel());
			super.deactivate();
		}
	}

	private WhiteboardDiagram getCastedModel() {
		return (WhiteboardDiagram) getModel();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#getModelChildren()
	 */
	protected List getModelChildren() {
		List<EObject> children = new ArrayList<EObject>();
		children.addAll(getCastedModel().getShapes());
		children.addAll(getCastedModel().getFreeWritingShapes());
		return children; // return a list of shapes
	}

	/**
	 * EditPolicy for the Figure used by this edit part.
	 * Children of XYLayoutEditPolicy can be used in Figures with XYLayout.
	 * @author Elias Volanakis
	 */
	private class ShapesXYLayoutEditPolicy extends XYLayoutEditPolicy {

		/* (non-Javadoc)
		 * @see org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#createAddCommand(org.eclipse.gef.EditPart, java.lang.Object)
		 */
		protected Command createAddCommand(EditPart child, Object constraint) {
			// not used in this example
			return null;
		}


		/* (non-Javadoc)
		 * @see org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#createChangeConstraintCommand(org.eclipse.gef.requests.ChangeBoundsRequest, org.eclipse.gef.EditPart, java.lang.Object)
		 */
		protected Command createChangeConstraintCommand(ChangeBoundsRequest request,
				EditPart child, Object constraint) {
			if (child instanceof ShapeEditPart && constraint instanceof Rectangle) {
				// return a command that can move and/or resize a Shape
				return new ShapeSetConstraintCommand(
						(Shape) child.getModel(), request, (Rectangle) constraint);
			} else if (child instanceof FreeWritingShapeEditPart 
								&&  constraint instanceof Rectangle) {
				// return a command that can add a Shape to a ShapesDiagram 
				return new FreeWritingShapeSetConstraintCommand(
						(FreeWritingShape) child.getModel(), request, (Rectangle) constraint);
			}
			return super.createChangeConstraintCommand(request, child, constraint);
		}

		/* (non-Javadoc)
		 * @see org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#createChangeConstraintCommand(org.eclipse.gef.EditPart, java.lang.Object)
		 */
		protected Command createChangeConstraintCommand(EditPart child,
				Object constraint) {
			// not used in this example
			return null;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.gef.editpolicies.LayoutEditPolicy#getCreateCommand(org.eclipse.gef.requests.CreateRequest)
		 */
		protected Command getCreateCommand(CreateRequest request) {
			Object childClass = request.getNewObject();
			if (childClass instanceof EllipticalShape || childClass instanceof RectangularShape
					|| childClass instanceof StickyNote) {
				// return a command that can add a Shape to a ShapesDiagram 
				return new ShapeCreateCommand(
						WhiteboardDiagramEditPart.this.getCastedModel(), 
						request,
						(Rectangle) getConstraintFor(request));
				
			} else if (childClass instanceof FreeWritingShape &&  request instanceof FreeHandwritingCreateRequest) {
				// return a command that can add a Shape to a ShapesDiagram 
				return new FreeWritingShapeCreateCommand(
						WhiteboardDiagramEditPart.this.getCastedModel(),
						(FreeHandwritingCreateRequest) request,
						(Rectangle) getConstraintFor(request));
			}
 
			return null;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.gef.editpolicies.LayoutEditPolicy#getDeleteDependantCommand(org.eclipse.gef.Request)
		 */
		protected Command getDeleteDependantCommand(Request request) {
			// not used in this example
			return null;
		}
		
		@Override
		public void showTargetFeedback(Request request) {
			if (request instanceof FreeHandwritingCreateRequest) {
				FreeHandwritingCreateRequest freeHandReq = (FreeHandwritingCreateRequest) request;
				Rectangle bound = (Rectangle) getConstraintFor(freeHandReq);
				List <XY> points = freeHandReq.getPoints();
				if(points != null) {
					if(line == null) {
						line = new Polyline();
						getFigure().add(line);
					}
					line.removeAllPoints();
					for(int i=0; i < points.size(); i++) {
						XY  xy = points.get(i);
						int diffX = bound.x - freeHandReq.getLocation().x;
						int diffY = bound.y - freeHandReq.getLocation().y;
						Point p = new Point(xy.getX() + diffX, xy.getY() + diffY);
						line.addPoint(p);
					}

					line.repaint();
				}
				
			} else {
				super.showTargetFeedback(request);
			}
		}
		
		@Override
		public void eraseTargetFeedback(Request request)
		{
			try {
				if(request instanceof FreeHandwritingCreateRequest) {
					if(line != null && getFigure().getChildren().contains(line))
						getFigure().remove(line);
					line = null;
				}
				else
					super.eraseTargetFeedback(request);
			} catch (Exception e) {
				Debug.log_error(WhiteboardPlugin.PLUGIN_ID, "Erase Target Feedback error");
				Debug.log_error(WhiteboardPlugin.PLUGIN_ID, e.getMessage());
			}
		}
		
	}

	private void hookIntoModel(EObject model) {
		if(model != null) {
			model.eAdapters().add(this);
		}
	}

	private void unhookFromModel(EObject model) {
		if(model != null) {
			model.eAdapters().remove(this);
		}
	}

	public void notifyChanged(Notification notification) {
		refreshChildren();
	}

	public Notifier getTarget() {
		return target;
	}

	public void setTarget(Notifier newTarget) {
		target = newTarget;
	}

	public boolean isAdapterForType(Object type) {
		return (getModel().getClass() == type);
	}
	
//	protected DirectEditPolicy createDirectEditPolicy() {
//		return new DirectEditPolicy() {
//			protected Command getDirectEditCommand(DirectEditRequest request) {
//				return new ChangeTextCommand((StickyNote)getModel(), 
//						(String)request.getCellEditor().getValue());
//			}
//			protected void showCurrentEditValue(DirectEditRequest request) {
//				((StickyNoteFigure)getFigure()).setText(
//						(String)request.getCellEditor().getValue());
//				getFigure().getUpdateManager().performUpdate();
//			}
//		};
//	}
	
	protected void handlePropertyChanged(Notification msg) {
//		switch (msg.getFeatureID(Node.class)) {
//			case ModelPackage.NODE__LOCATION:
//			case ModelPackage.NODE__WIDTH:
//				refreshVisuals();
//				break;
//			case ModelPackage.NODE__OUTGOING_CONNECTIONS:
//				refreshSourceConnections();
//				break;
//			case ModelPackage.NODE__INCOMING_CONNECTIONS:
//				refreshTargetConnections();
//				break;
//		}
	}
	
	private Polyline line = null;

}